﻿using System;
using System.Reflection.Emit;

namespace Aeon.Emulator.Decoding.Emitters
{
    /// <summary>
    /// Emitter for decoding a register in the ModR/M Reg field.
    /// </summary>
    internal sealed class LoadRegister : Emitter
    {
        #region Constructors
        /// <summary>
        /// Initializes a new instance of the LoadRegister class.
        /// </summary>
        /// <param name="state">Common emitter state information.</param>
        /// <param name="registerSize">Size of the register to load in bytes.</param>
        public LoadRegister(EmitStateInfo state, int registerSize)
            : base(state)
        {
            if(registerSize != 1 && registerSize != 2 && registerSize != 4)
                throw new ArgumentException("Invalid register size.", "registerSize");

            this.RegisterSize = registerSize;
        }
        #endregion

        #region Public Properties
        /// <summary>
        /// Gets the size of the register to load in bytes.
        /// </summary>
        public int RegisterSize { get; private set; }
        /// <summary>
        /// Gets the type of local variable needed to store the operand value as a method argument.
        /// </summary>
        public override Type MethodArgType
        {
            get
            {
                var type = GetUnsignedIntType(this.RegisterSize);

                if(this.ReturnType == EmitReturnType.Address)
                    return type.MakePointerType();
                else
                    return type;
            }
        }
        #endregion

        #region Public Methods
        /// <summary>
        /// Emits IL for the operation.
        /// </summary>
        public override void EmitLoad()
        {
            //// Reg is the middle 3 bits of the ModR/M byte.
            //int reg = (*ip & 0x38) >> 3;

            LoadProcessor();

            //il.LoadLocal(ipPointer);
            LoadIPPointer();
            il.Emit(OpCodes.Ldind_U1);
            il.LoadConstant(0x38);
            il.Emit(OpCodes.And);
            il.LoadConstant(3);
            il.Emit(OpCodes.Shr_Un);

            bool byteVersion = this.RegisterSize == 1;
            bool returnValue = this.ReturnType == EmitReturnType.Value;

            if(!byteVersion)
            {
                il.Emit(OpCodes.Call, Infos.Processor.GetRegisterWordPointer);
                if(returnValue)
                    il.Emit(this.RegisterSize == 4 ? OpCodes.Ldind_U4 : OpCodes.Ldind_U2);
            }
            else
            {
                il.Emit(OpCodes.Call, Infos.Processor.GetRegisterBytePointer);
                if(returnValue)
                    il.Emit(OpCodes.Ldind_U1);
            }
        }
        #endregion
    }
}
